package view;

import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import model.Model;
import model.SyntaxErrorException;


/**
 * This is the user input terminal.
 * It calls the commands into the user input.
 * 
 * @author Matthew Roy
 * 
 */
public class CommandTerminal extends JPanel {

    /**
     * text field size.
     */
    public static final int FIELD_SIZE = 30;
    /**
     * Constant for manual movement.
     */
    private static final int MANUAL_MOVEMENT_VALUE = 50;
    /**
     * Constant for manual rotation.
     */
    private static final int MANUAL_ROTATION_VALUE = 45;
    /**
     * Height of the Text Area.
     */
    private static final int TEXT_AREA_HEIGHT = 35;
    /**
     * Width of the Text Area.
     */
    private static final int TEXT_AREA_WIDTH = 25;
    /**
     * constant error message.
     */
    private static final String ERROR_MESSAGE = "Error executing command";
    
    private static final long serialVersionUID = 1L;
    /**
     * resources location.
     */
    private ResourceBundle myResources = ResourceBundle.
            getBundle("resources." + "English");
    private FocusListener myFocusListener;
    /**
     * ActionListeners.
     */
    private KeyListener myKeyListener;
    private MouseListener myMouseListener;
    /**
     * button to enter command.
     */
    private JButton myEnterButton;
    /**
     * text area where command history is stored.
     */
    private JTextArea myHistory;
    /**
     * button to toggle manual control on and off.
     */
    private JToggleButton myManualControl;
    /**
     * tells whether manual control is on or off.
     */
    private boolean myManualControlToggle;
    /**
     * Model for this CommandTerminal.
     */
    private Model myModel;
    /**
     * list of previous commands entered by the user.
     */
    private List<String> myPreviousCommands;
    /**
     * text field where user will type commands.
     */
    private JTextField myUserTerminal;
    
    /**
     * constructor.
     * 
     * @param size is the size of the terminal
     * @param m is the associated model
     */
    public CommandTerminal (Dimension size, Model m) {
        setPreferredSize(size);
        myModel = m;
        myManualControlToggle = false;
        makeListeners();

        // Add components
        myUserTerminal = makeTextField();
        add(myUserTerminal);
        myEnterButton = makeButton("EnterCommand");
        add(myEnterButton);
        myManualControl = makeToggleButton("Manual Control");
        add(myManualControl);
        myHistory = makeTextArea();
        add(new JScrollPane(myHistory));

        myPreviousCommands = new ArrayList<String>();
    }
    
    /**
     * gets the associated command keyword from the resources.
     * @param command
     * @return the String for this command
     */
     public String getCommandName(String command) {
         String temp = myResources.getString(command);
         int index = temp.indexOf("|");
         if (index == -1) {
             return temp;
         }
         return temp.substring(0, index);
     }

    /**
     * Gets a list of the previous commands the
     * user entered.
     * 
     * @return a list of previous commands
     */
    public ArrayList<String> getPreviousCommands () {
        return new ArrayList<String>(myPreviousCommands);
    }

    /**
     * Converts user key commands into directional signals.
     */
    private void interpreteManualInput (KeyEvent e) {
        if (!myManualControlToggle) { return; }
        int key = e.getKeyCode();
        String cmd = null;
        if (key == KeyEvent.VK_UP) {
            cmd = getCommandName("ForwardCommandPattern") + " "
                  + MANUAL_MOVEMENT_VALUE;
        }
        else if (key == KeyEvent.VK_DOWN) {
            cmd = getCommandName("BackwardCommandPattern") + " "
                  + MANUAL_MOVEMENT_VALUE;
        }
        else if (key == KeyEvent.VK_RIGHT) {
            cmd = getCommandName("RightCommandPattern") + " "
                  + MANUAL_ROTATION_VALUE;
        }
        else if (key == KeyEvent.VK_LEFT) {
            cmd = getCommandName("LeftCommandPattern") + " "
                  + MANUAL_ROTATION_VALUE;
        }
        else {
            return;
        }
        sendCommand(cmd);
    }

    /**
     * Makes a button.
     * 
     * @param name is the text to display on the
     *        button.
     * @return the JButton
     */
    private JButton makeButton (String name) {
        JButton result = null;
        if (myResources.containsKey(name)) {
            result = new JButton(myResources.getString(name));
        }
        else {
            result = new JButton(name);
        }
        result.addKeyListener(myKeyListener);
        result.addMouseListener(myMouseListener);
        return result;
    }

    /**
     * Makes the area where the history of commands
     * will display.
     * 
     * @return the JTextArea
     */
    private JTextArea makeTextArea () {
        JTextArea result = new JTextArea(TEXT_AREA_WIDTH, TEXT_AREA_HEIGHT);
        result.setEditable(false);
        result.setFocusable(true);
        result.addMouseListener(myMouseListener);
        return result;
    }

    /**
     * Makes the area where the user can type
     * commands.
     * 
     * @return the JTextField
     */
    private JTextField makeTextField () {
        JTextField result = new JTextField(FIELD_SIZE);
        myUserTerminal = result;
        result.addKeyListener(myKeyListener);
        result.addFocusListener(myFocusListener);
        return result;
    }
    
    /**
     * Makes the button to toggle manual control.
     * @param name is the name of the button
     * @return JToggleButton
     */
    private JToggleButton makeToggleButton (String name) {
        JToggleButton result = null;
        if (myResources.containsKey(name)) {
            result = new JToggleButton(myResources.getString(name));
        }
        else {
            result = new JToggleButton(name);
        }
        result.addKeyListener(myKeyListener);
        result.addMouseListener(myMouseListener);
        return result;
    }

    /**
     * Makes the listers so the command terminal screen
     * can respond to input.
     */
    private void makeListeners () {
        // listener for "high-level" events, i.e., those made
        // up of a sequence of low-level events, like a button
        // press (mouse down and up within a button object)
        // listener for low-level keyboard events
        myKeyListener = new KeyListener() {
            @Override
            public void keyPressed (KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    readAndSendUserPromptCommand();
                }
                else if (myManualControlToggle) {
                    interpreteManualInput(e);
                }
            }
            @Override
            public void keyReleased (KeyEvent e) {
            }
            @Override
            public void keyTyped (KeyEvent arg0) {
            }
        };
        myMouseListener = new MouseListener() {
            @Override
            public void mouseClicked (MouseEvent e) {
                if (e.getSource().equals(myEnterButton)) {
                    readAndSendUserPromptCommand();
                }
                else if (e.getSource().equals(myManualControl)) {
                    toggleManualDriveMode();
                }
                else if (e.getSource().equals(myHistory) &&
                         e.getClickCount() == 3) {
                    sendCommand(myHistory.getSelectedText());
                }
            }
            @Override
            public void mouseEntered (MouseEvent e) {
            }
            @Override
            public void mouseExited (MouseEvent e) {
            }
            @Override
            public void mousePressed (MouseEvent e) {
            }
            @Override
            public void mouseReleased (MouseEvent e) {
            }
        };
        // listener for low-level focus events, i.e., the mouse
        // entering/leaving a component so you can type in it
        myFocusListener = new FocusListener() {
            @Override
            public void focusGained (FocusEvent e) {
            }

            @Override
            public void focusLost (FocusEvent e) {
            }
        };
    }

    /**
     * Gets the input from the user and send it
     * to the model.
     */
    private void readAndSendUserPromptCommand () {
        String command = myUserTerminal.getText();
        myUserTerminal.setText("");
        sendCommand(command);
    }

    /**
     * Sends the command to the interpreter.
     * 
     * @param command is the command as a String
     */
    public void sendCommand (String command) {
        try {
            int output = sendCommandToParser(command);
            myPreviousCommands.add(command);
            showMessage("> " + output);
            showMessage(command);
        }
        catch (SyntaxErrorException e) {
            showMessage(ERROR_MESSAGE);
        }
    }

    /**
     * Sends the user entered command to the model
     * to be executed.
     * 
     * @param command the string the user entered
     * @return true if no errors, false if it had an error
     * @throws SyntaxErrorException 
     */
    private int sendCommandToParser (String command) throws SyntaxErrorException {        
        return myModel.update(command);
    }

    /**
     * Display any string message in the main text area.
     * 
     * @param message message to display
     */
    public void showMessage (String message) {
        // Places message at the top of the text box
        myHistory.insert(message + "\n", 0);
        myHistory.setCaretPosition(myHistory.getText().length());
    }

    /**
     * Turns the arrow key driving on or off.
     */
    private void toggleManualDriveMode () {
        myManualControlToggle = !myManualControlToggle;
    }
}
